#include	"a_global_vars.h"

struct timeval startTime, endTime, end, start, performance_start_time, performance_end_time, tv1, tv2;
struct sockaddr_in *ipmAddr;
struct sockaddr_in ring_successor_addr, ring_self_addr;
struct event *timeout_ev1, *timeout_ev2, *socketEve1, *socketEve2, *socketEve3;

int self_id, SUBMIT_PER_WAKE;
int lowestDelivered = -1, gapEnd = -1;
bool COORDINATOR = false, MULTICASTER = false;
struct event *learners_sockEvent;
inst *data;
paxos_data* paxos_instances;

inst *new_data;
const char * config_file_name;
//------------------------------------------------------------------------------
void validate_input_parameters(int argc, const char *argv[]);
void recv_new_packet(int sd, short event, void * arg);
void main_initialize();
void common_event_init();
void initialize();
void clean_exit(int sig);
void enable_ctrl_c_handler();
//------------------------------------------------------------------------------
int main(int argc, const char *argv[]) {
	unsigned int len_addr;
	int i, status;
	enable_ctrl_c_handler();
	validate_input_parameters(argc, argv);
	parse_config_file(config_file_name, self_id);
	main_initialize();

	ipmAddr = GET_SOCKADDR();
	event_init();
	len_addr = sizeof(ipmAddr);
	initialize();
	COORDINATOR = false;
	MULTICASTER = false;
	common_event_init();

	if (get_config_info()->self_id == 0) {
		MULTICASTER = true;
		evtimer_set(multicast_timeout_eve, multicast_instance, multicast_timeout_eve);
		status = evtimer_add(multicast_timeout_eve, &tv1);
		assert(status >= 0);

		event_set(proposer_get_value_eve, get_config_info()->server_infos[self_id].proposer_recv_sid, EV_READ | EV_PERSIST, get_msg, NULL);
		status = event_add(proposer_get_value_eve, NULL);
		assert(status >= 0);

	}
	int event_c = 0;
	if (get_config_info()->self_id == 1) {
		COORDINATOR = true;


		for (i = 0; i < get_config_info()->total_num_learners; i++) {
			if (get_config_info()->learner_infos[i].acceptor_id == self_id) {
				printf("%d\n", get_config_info()->learner_infos[i].learner_to_acceptor_sid);
				event_set(&learners_sockEvent[event_c], get_config_info()->learner_infos[i].learner_to_acceptor_sid, EV_READ | EV_PERSIST, get_learner_request, NULL);
				status = event_add(&learners_sockEvent[event_c], NULL);
				assert(status >= 0);
				event_c++;
			}
		}
	}
	printf("events: %d\n", event_c);
	event_dispatch();
	return 0;
}
//------------------------------------------------------------------------------
void main_initialize() {
	recv_ring_sock_eve = (struct event*) malloc(sizeof(struct event));
	recv_multicast_sock_eve = (struct event*) malloc(sizeof(struct event));
	multicast_timeout_eve = (struct event*) malloc(sizeof(struct event));
	print_stat_timeout_eve = (struct event*) malloc(sizeof(struct event));
	proposer_get_value_eve = (struct event*) malloc(sizeof(struct event));
	learners_sockEvent = (struct event*) malloc(sizeof(struct event) * get_config_info()->num_learners_to_connect_to_one_acceptor);
	tv1.tv_sec = 0;
	tv2.tv_sec = 1;
	tv2.tv_usec = 0;

}
//------------------------------------------------------------------------------
void common_event_init() {
	int status;

	if (get_config_info()->self_id > 0) {
		event_set(recv_multicast_sock_eve, get_config_info()->server_infos[self_id].multicast_recv_sid, EV_READ | EV_PERSIST, recv_new_packet, NULL);
		status = event_add(recv_multicast_sock_eve, NULL);
		assert(status >= 0);
	}
	if (get_config_info()->self_id != 1) {
		event_set(recv_ring_sock_eve, get_config_info()->server_infos[self_id].self_sid, EV_READ | EV_PERSIST, acceptor_phase_recognizer, NULL);
		status = event_add(recv_ring_sock_eve, NULL);
		assert(status >= 0);
	}

	printf("-----------------------------------------------------------\n");
	evtimer_set(print_stat_timeout_eve, compute_performance, print_stat_timeout_eve);
	status = evtimer_add(print_stat_timeout_eve, &tv2);
	assert(status >= 0);

	gettimeofday(&start, NULL);
	gettimeofday(&current, NULL);
	gettimeofday(&last, NULL);
	gettimeofday(&startTime, NULL);
	gettimeofday(&performance_start_time, NULL);

}
//------------------------------------------------------------------------------
void initialize() {
	a_interval_performance = malloc(sizeof(a_performance));
	a_next_performance_index = 0;
	a_nulify_performance(a_interval_performance);

	a_interval_performance->highest_deliverable_inst_id = -1;
	int i;

	data = (inst*) malloc(sizeof(inst) * INTERNAL_BUFFER_SIZE);
	paxos_instances = (paxos_data*) malloc(sizeof(paxos_data) * INTERNAL_BUFFER_SIZE);

	for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
		data[(i % INTERNAL_BUFFER_SIZE)].deliver = false;
		data[(i % INTERNAL_BUFFER_SIZE)].received = false;
		data[(i % INTERNAL_BUFFER_SIZE)].highestDeliverable = -1;
		data[(i % INTERNAL_BUFFER_SIZE)].packet.msgs[0].msg_id = 0;
	}

	if (!COORDINATOR) {
		for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].crnd = -1;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].cval = -1;
		}
	} else {
		for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].crnd = 1;// before =0
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].cval = 0;
		}
	}
	accept_msg = (paxos_msg*) malloc(sizeof(paxos_msg));
	new_data = (inst*) malloc(sizeof(inst));
}

void validate_input_parameters(int argc, const char *argv[]) {
	if (argc != 7) {
		printf("Incompatible call to this function. Try Again.!\n"
			"<1. Process ID>\n"
			"<2. Config file >\n"
			"<3. Num of Packets>\n"
			"<4. Interval>\n"
			"<5. Performance File>\n"
			"<6. Latency File>\n");
		exit(1);
	} else {
		self_id = atoi(argv[1]);
		config_file_name = argv[2];
		SUBMIT_PER_WAKE = atoi(argv[3]);
		tv1.tv_usec = atoi(argv[4]);
		a_performance_file = argv[5];
		a_latency_file = argv[6];
	}
}

void clean_exit(int sig) {
	signal(sig, clean_exit);
	printf("Caught exit signal\n");
	a_store_performance_in_file(a_performance_file, a_latency_file, a_next_performance_index);
	close(get_config_info()->server_infos[self_id].self_sid);
	close(get_config_info()->server_infos[self_id].successor_sid);
	free(data);
	exit(0);
}
void enable_ctrl_c_handler() {
	signal(SIGINT, clean_exit);
	signal(SIGKILL, clean_exit);
	signal(SIGABRT, clean_exit);
	signal(SIGALRM, clean_exit);
	signal(SIGBUS, clean_exit);
	signal(SIGCHLD, clean_exit);
	signal(SIGCONT, clean_exit);
	signal(SIGFPE, clean_exit);
	signal(SIGHUP, clean_exit);
	signal(SIGILL, clean_exit);
	signal(SIGPIPE, clean_exit);
	signal(SIGQUIT, clean_exit);
	signal(SIGSEGV, clean_exit);
	signal(SIGSTOP, clean_exit);
	signal(SIGTERM, clean_exit);
	signal(SIGTSTP, clean_exit);
	signal(SIGTTIN, clean_exit);
	signal(SIGTTOU, clean_exit);
	signal(SIGUSR1, clean_exit);
	signal(SIGUSR2, clean_exit);
	signal(SIGPROF, clean_exit);
	signal(SIGSYS, clean_exit);
	signal(SIGTRAP, clean_exit);
	signal(SIGURG, clean_exit);
	signal(SIGVTALRM, clean_exit);
	signal(SIGXCPU, clean_exit);
	signal(SIGXFSZ, clean_exit);

}
